#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "Common.h"
#include "Window.h"
#include "Internals.h"
#include "CtrlClass.h"

#include "Assert.h"

HWND GUIAPI CreateMainWindowIndirectParam (PDLGTEMPLATE pDlgTemplate
                                           , WNDPROC WndProc)
{
    PMAINWIN pWin;

    if(pDlgTemplate == NULL)
    {
        return HWND_INVALID;
    }

    if (!(pWin = calloc(1, sizeof(MAINWIN))))
    {
        return HWND_INVALID;
    }
	
    //LG_ASSERT(0);
    pWin->x             = pDlgTemplate->x;
    pWin->y             = pDlgTemplate->y;
    pWin->width         = pDlgTemplate->w;
    pWin->height        = pDlgTemplate->h;
    pWin->pMessages = __lg_dsk_msg_queue;
    pWin->pMainWin      = pWin;
    pWin->hParent       = 0;
    pWin->DataType      = TYPE_HWND;
    pWin->WinType       = TYPE_MAINWIN;
    pWin->dwStyle       = pDlgTemplate->dwStyle;

    if (pDlgTemplate->caption) {
        int len = strlen (pDlgTemplate->caption);

        if (len > 0)
        {
            pWin->spCaption = calloc(1, (len + 1));
            strcpy (pWin->spCaption, pDlgTemplate->caption);
        }
    }
    else
        pWin->spCaption = "";
    
    pWin->MainWindowProc = WndProc ? WndProc : DefaultMainWinProc;

    if ( !( pWin->pZOrderNode = malloc (sizeof(ZORDERNODE))) )
    {
        goto err;
    }

    //LG_ASSERT(0);
    SendMessage ((HWND)pWin, MSG_CHANGESIZE, (WPARAM)&pWin->x, 0);
    //LG_ASSERT(0);
    SendMessage (HWND_DESKTOP, MSG_ADDNEWMAINWIN, (WPARAM) pWin,
                 (LPARAM) pWin->pZOrderNode);
    //LG_ASSERT(0);

    //Uart0_Printf("pWin->dwStyle = %x\r\n", pWin->dwStyle);
    /* There was a very large bug.
     * We should add the new main window in system and then
     * SendMessage MSG_CREATE for application to create
     * child windows.
     */
    if (SendMessage ((HWND)pWin, MSG_CREATE, 0, (LPARAM)0))
    {
        LG_ASSERT(0);
        SendMessage(HWND_DESKTOP, MSG_REMOVEMAINWIN, (WPARAM)pWin, 0);
        goto err;
    }

    if(pWin->dwStyle & WS_VISIBLE)
    {
        SendMessage ((HWND)pWin, MSG_PAINT, 0, 0);
    }

    LG_ASSERT(0);
    return (HWND)pWin;

err:
    free (pWin);

    return HWND_INVALID;
}

BOOL GUIAPI DestroyMainWindowIndirect (HWND hWnd)
{
    PMAINWIN pWin;

    if (!(pWin = CheckAndGetMainWindowPtr (hWnd)))
    {
        return FALSE;
    }

    if (SendMessage (hWnd, MSG_DESTROY, 0, 0))
    {
        return FALSE;
    }

    /* destroy all controls of this window */
    DestroyAllControls (hWnd);

    /* kill all timers of this window */
    KillTimer (hWnd, 0);

    SendMessage(HWND_DESKTOP, MSG_REMOVEMAINWIN, (WPARAM)hWnd, 0);

    /* make the window to be invalid for PeekMessageEx, PostMessage etc */
    pWin->DataType = TYPE_WINTODEL;

    ThrowAwayMessages (hWnd);

    if (pWin->pZOrderNode)
    {
        free (pWin->pZOrderNode);
        pWin->pZOrderNode = NULL;
    }

    if (pWin->spCaption && pWin->spCaption[0] != '\0')
    {
        free (pWin->spCaption);
        pWin->spCaption = NULL;
    }

    free(pWin);
    return TRUE;
}

void GUIAPI DestroyAllControls (HWND hDlg)
{
    PMAINWIN pDlg = (PMAINWIN)hDlg;
    PCONTROL pCtrl = (PCONTROL)(pDlg->hFirstChild);
    PCONTROL pNext;

    while (pCtrl) {
        LG_ASSERT(0);
        pNext = pCtrl->next;
        DestroyWindow ((HWND)pCtrl);
        pCtrl = pNext;
    }
}


HWND GUIAPI CreateWindow (const char* spClassName,
                          const char* spCaption, DWORD dwStyle,
                          int x, int y, int w, int h, HWND hParentWnd)
{
    PMAINWIN pMainWin;
    PCTRLCLASSINFO cci;
    PCONTROL pNewCtrl;

    if (!(pMainWin = GetMainWindowPtrOfControl (hParentWnd)))
        return HWND_INVALID;

    cci = (PCTRLCLASSINFO)SendMessage (HWND_DESKTOP,
                                       MSG_GETCTRLCLASSINFO, 0, (LPARAM)spClassName);

    if (!cci) return HWND_INVALID;

    pNewCtrl = calloc (1, sizeof (CONTROL));

    if (!pNewCtrl) return HWND_INVALID;

    pNewCtrl->DataType = TYPE_HWND;
    pNewCtrl->WinType  = TYPE_CONTROL;

    pNewCtrl->x     = x + pMainWin->x;
    pNewCtrl->y      = y + pMainWin->y;
    pNewCtrl->width    = (w > (pMainWin->width - x)) ? (pMainWin->width - x): w;
    pNewCtrl->height   = (h > (pMainWin->height - y)) ? (pMainWin->height - y): h;

    if (spCaption) {
        int len = strlen (spCaption);

        if (len > 0)
        {
            pNewCtrl->spCaption = calloc(1, (len + 1));
            strcpy (pNewCtrl->spCaption, spCaption);
        }
    }
    else
        pNewCtrl->spCaption = "";

    pNewCtrl->dwStyle   = dwStyle | WS_CHILD | cci->dwStyle;

    pNewCtrl->ControlProc = cci->ControlProc;
    pNewCtrl->children = NULL;
    pNewCtrl->pMainWin = (PMAINWIN)pMainWin;
    pNewCtrl->pParent  = (PCONTROL)hParentWnd;
    pNewCtrl->next     = NULL;

    pNewCtrl->pcci     = cci;

    if (SendMessage (HWND_DESKTOP,
                     MSG_NEWCTRLINSTANCE,
                     (WPARAM)hParentWnd, (LPARAM)pNewCtrl) < 0)
    {
        goto error;
    }

    if (SendMessage ((HWND)pNewCtrl, MSG_CREATE,
                     (WPARAM)hParentWnd, (LPARAM)0))
    {
        LG_ASSERT(0);
        SendMessage (HWND_DESKTOP,
                     MSG_REMOVECTRLINSTANCE, (WPARAM)hParentWnd, (LPARAM)pNewCtrl);
        goto error;
    }

    LG_ASSERT(0);

    if (pNewCtrl->pMainWin->dwStyle & WS_VISIBLE
            && pNewCtrl->dwStyle & WS_VISIBLE)
    {
        SendMessage ((HWND)pNewCtrl, MSG_PAINT, 0, 0);
        LG_ASSERT(0);
    }

    return (HWND)pNewCtrl;

error:
    free (pNewCtrl);

    return HWND_INVALID;
}

BOOL GUIAPI DestroyWindow (HWND hWnd)
{
    PCONTROL pCtrl;
    PCONTROL pParent;

    if (!IsControl (hWnd))
    {
        return FALSE;
    }

    if (SendMessage (hWnd, MSG_DESTROY, 0, 0))
    {
        return FALSE;
    }

    pCtrl = (PCONTROL)hWnd;
    pParent = pCtrl->pParent;

    LG_ASSERT(0);
    if (SendMessage (HWND_DESKTOP,
                     MSG_REMOVECTRLINSTANCE, (WPARAM)pParent, (LPARAM)pCtrl))
    {
        LG_ASSERT(0);
        return FALSE;
    }

    LG_ASSERT(0);

    pCtrl->dwStyle &= ~WS_VISIBLE;
    ThrowAwayMessages (hWnd);

    if (pCtrl->spCaption && pCtrl->spCaption[0] != '\0')
    {
        free (pCtrl->spCaption);
    }

    free (pCtrl);
    return TRUE;
}


int GUIAPI PreDefMainWinProc (HWND hWnd, int message,
                              WPARAM wParam, LPARAM lParam)
{
    PMAINWIN pWin = (PMAINWIN)hWnd;
    RECT    rcClient;

    switch(message)
    {
        case MSG_CHANGESIZE:
            GetClientRect(hWnd, &rcClient);
            //LG_ASSERT(0);
            GUIClearRect(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
            //LG_ASSERT(0);
        break;
        case MSG_PAINT:
            GetClientRect(hWnd, &rcClient);
            //LG_ASSERT(0);
            if(pWin->dwStyle & WS_BORDER)
            {
                //Uart0_Printf("MSG_PAINT %d  %d  %d  %d\r\n", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
                GUIDrawRect(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
            }
        break;
        case MSG_DESTROY:
            GetClientRect(hWnd, &rcClient);
            GUIClearRect(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
        break;
    }

    //LG_ASSERT(0);
    return 0;
}

int GUIAPI PreDefControlProc (HWND hWnd, int message,
                              WPARAM wParam, LPARAM lParam)
{
    PCONTROL  pCtrl = (PCONTROL)hWnd;
    RECT rcClient;

    switch(message)
    {
        case MSG_PAINT:
            GetClientRect(hWnd, &rcClient);

            if(pCtrl->dwStyle & WS_BORDER)
            {
                GUIDrawRect(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
            }
        break;
        case MSG_DESTROY:
            GetClientRect(hWnd, &rcClient);
            GUIClearRect(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
        break;
    }
}

int GUIAPI DefaultWindowProc (HWND hWnd, int message,
                              WPARAM wParam, LPARAM lParam)
{
    if (IsMainWindow(hWnd)) {
        return DefaultMainWinProc (hWnd, message, wParam, lParam);
    }
    else if (IsControl(hWnd)) {
        return DefaultControlProc (hWnd, message, wParam, lParam);
    }

    return 0;
}

/*************************** Main window and thread **************************/

/* get main window pointer from a handle */
PMAINWIN CheckAndGetMainWindowPtr (HWND hWnd)
{
    MG_CHECK_RET (MG_IS_NORMAL_MAIN_WINDOW(hWnd), NULL);
    return MG_GET_WINDOW_PTR (hWnd);
}

PMAINWIN GetMainWindowPtrOfControl (HWND hWnd)
{
    PMAINWIN pWin;

    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), NULL);
    pWin = MG_GET_WINDOW_PTR (hWnd);

    return pWin->pMainWin;
}

BOOL GUIAPI IsWindow (HWND hWnd)
{
    return MG_IS_NORMAL_WINDOW (hWnd);
}

BOOL GUIAPI IsMainWindow (HWND hWnd)
{
    PMAINWIN pWin;

    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), FALSE);

    pWin = MG_GET_WINDOW_PTR(hWnd);

    return (pWin->WinType == TYPE_MAINWIN);
}

BOOL GUIAPI IsControl (HWND hWnd)
{
    PMAINWIN pWin;

    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), FALSE);

    pWin = MG_GET_WINDOW_PTR(hWnd);

    return (pWin->WinType == TYPE_CONTROL);
}

BOOL GUIAPI IsWindowEnabled (HWND hWnd)
{
    PMAINWIN pWin = (PMAINWIN)hWnd;

    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), FALSE);

    return !(pWin->dwStyle & WS_DISABLED);
}

/* NOTE: this function support ONLY main window. */
HWND GUIAPI GetActiveWindow (void)
{
    return (HWND)SendMessage (HWND_DESKTOP, MSG_GETACTIVEMAIN, 0, 0);
}

int GUIAPI GetWindowTextLength (HWND hWnd)
{
    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), -1);

    return SendMessage (hWnd, MSG_GETTEXTLENGTH, 0, 0);
}

int GUIAPI GetWindowText (HWND hWnd, char* spString, int nMaxLen)
{
    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), -1);

    return SendMessage (hWnd, MSG_GETTEXT, (WPARAM)nMaxLen, (LPARAM)spString);
}

BOOL GUIAPI SetWindowText (HWND hWnd, const char* spString)
{
    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), FALSE);

    return (SendMessage (hWnd, MSG_SETTEXT, 0, (LPARAM)spString) == 0);
}

BOOL GUIAPI GetClientRect (HWND hWnd, PRECT prc)
{
    static RECT g_rcScr = {0, 0, 131, 63};
    PMAINWIN pWin = (PMAINWIN)hWnd;

    if (hWnd == HWND_DESKTOP)
    {
        *prc = g_rcScr;
        return TRUE;
    }
    else if (hWnd == HWND_INVALID || pWin->DataType != TYPE_HWND)
    {
        return FALSE;
    }

    prc->left = pWin->x;
    prc->top = pWin->y;
    prc->right = pWin->x + pWin->width -1;
    prc->bottom = pWin->y+ pWin->height-1;
    return TRUE;
}

DWORD GUIAPI GetWindowStyle (HWND hWnd)
{
    PMAINWIN pWin;

    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), 0);
    pWin = MG_GET_WINDOW_PTR (hWnd);

    return pWin->dwStyle;
}

const char* GUIAPI GetWindowCaption (HWND hWnd)
{
    PMAINWIN pWin;

    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), NULL);
    pWin = MG_GET_WINDOW_PTR (hWnd);

    return pWin->spCaption;
}

BOOL GUIAPI SetWindowCaption (HWND hWnd, const char* spCaption)
{
    PMAINWIN pWin;

    MG_CHECK_RET (MG_IS_NORMAL_WINDOW(hWnd), FALSE);
    pWin = MG_GET_WINDOW_PTR (hWnd);

    if (pWin->WinType == TYPE_MAINWIN) {
        return SetWindowText (hWnd, spCaption);
    }
    else if (pWin->WinType == TYPE_CONTROL) 
    {
        PCONTROL pCtrl;
        pCtrl = (PCONTROL)hWnd;
        if (pCtrl->spCaption) 
        {
            free (pCtrl->spCaption);
            pCtrl->spCaption = NULL;
        }

        if (spCaption) 
        {            
            int len = strlen (spCaption);
            
            if (len > 0 && spCaption [0])
            {
                pCtrl->spCaption = calloc(1, (len + 1));
                strcpy (pCtrl->spCaption, spCaption);
            }
        }

        return TRUE;
    }

    return FALSE;
}



